import { defineComponent, shallowRef } from 'vue';
import { camelCase } from 'lodash';
import type { DefineComponent, Slot } from 'vue';

// 将横线命名转换为驼峰命名
function keysToCamelKebabCase(obj: Record<string, any>) {
  const newObj: typeof obj = {};
  for (const key in obj) newObj[camelCase(key)] = obj[key];
  return newObj;
}

// 定义 DefineTemplateComponent 类型，该类型表示定义模板的组件
export type DefineTemplateComponent<
  Bindings extends object,
  Slots extends Record<string, Slot | undefined>,
> = DefineComponent<object> & {
  new (): { $slots: { default(_: Bindings & { $slots: Slots }): any } };
};

// 定义 ReuseTemplateComponent 类型，该类型表示复用模板的组件
export type ReuseTemplateComponent<
  Bindings extends object,
  Slots extends Record<string, Slot | undefined>,
> = DefineComponent<Bindings> & {
  new (): { $slots: Slots };
};

// 定义 ReusableTemplatePair 类型，表示一个定义模板和复用模板的组件对
export type ReusableTemplatePair<
  Bindings extends object,
  Slots extends Record<string, Slot | undefined>,
> = [DefineTemplateComponent<Bindings, Slots>, ReuseTemplateComponent<Bindings, Slots>];

// useTemplate 函数，返回一个定义模板和复用模板的组件对
export const useTemplate = <
  Bindings extends object,
  Slots extends Record<string, Slot | undefined> = Record<string, Slot | undefined>,
>(): ReusableTemplatePair<Bindings, Slots> => {
  const render = shallowRef<Slot | undefined>();

  // 定义 DefineTemplateComponent 组件
  const define = defineComponent({
    setup(_, { slots }) {
      return () => {
        // 将复用模板的渲染函数内容保存起来
        render.value = slots.default;
      };
    },
  }) as DefineTemplateComponent<Bindings, Slots>;

  // 定义 ReuseTemplateComponent 组件
  const reuse = defineComponent({
    setup(_, { attrs, slots }) {
      return () => {
        // 还没定义复用模板，则抛出错误
        if (!render.value) {
          throw new Error('你还没定义复用模板呢！');
        }
        // 执行渲染函数，传入 attrs、slots
        const vnode = render.value({ ...keysToCamelKebabCase(attrs), $slots: slots });
        return vnode.length === 1 ? vnode[0] : vnode;
      };
    },
  }) as ReuseTemplateComponent<Bindings, Slots>;

  return [define, reuse];
};

/**
 // 前面是定义模版，后面是使用
 const [DefineTemplate, ReuseTemplate] = useTemplate();
 https://blog.csdn.net/qq_41961239/article/details/135651146
 <!-- 在本组件中定义一个复用模板 -->
 <DefineTemplate v-slot="{item}">
 <div class="nameItem">
 <FlexAuto3>
 <div class="single-line-ellipsis">
 {{item.label}}
 </div>
 </FlexAuto3>
 <el-button
 text
 icon="Delete"
 type="danger"
 v-if="checkAuths(['del']) && comFileList.length>1"
 @click.stop="deleteItem(item)"
 ></el-button>
 </div>
 </DefineTemplate>


 <ElSelectPlus
 v-model="selectFileName"
 :options="comFileList.map(i=> {return {value: i.fileName,label: i.fileName}})"
 @change="openFileSetRoute"
 filterable
 >
 <template #default="item">
 <!-- 可多次使用复用模板 -->
 <ReuseTemplate
 :item="item"
 />
 </template>
 </ElSelectPlus>
 */
